/*
 * Copyright (C) 2015 Baidu, Inc. All Rights Reserved.
 */
package com.melody.map.baidu_compose.utils.clustering.ui

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.Build
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import com.melody.map.baidu_compose.R

/**
 * IconGenerator generates icons that contain text (or custom content) within an info
 * window-like shape.
 *
 *
 * The icon [Bitmap]s generated by the factory should be used in conjunction with a [ ].
 *
 *
 * This class is not thread safe.
 */
internal class IconGenerator(private val mContext: Context) {
    private val mContainer: LinearLayout = LinearLayout(mContext)
    private val mRotationLayout: RotationLayout
    private var mTextView: TextView?
    private var mContentView: View
    private var mRotation = 0
    private val mAnchorU = 0.5f
    private val mAnchorV = 1f

    /**
     * Sets the text content, then creates an icon with the current style.
     *
     * @param text the text content to display inside the icon.
     */
    fun makeIcon(text: String?): Bitmap {
        mTextView?.text = text
        return makeIcon()
    }

    /**
     * Creates an icon with the current content and style.
     *
     *
     * This method is useful if a custom view has previously been set, or if text content is not
     * applicable.
     */
    private fun makeIcon(): Bitmap {
        val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        mContainer.measure(measureSpec, measureSpec)
        var measuredWidth = mContainer.measuredWidth
        var measuredHeight = mContainer.measuredHeight
        mContainer.layout(0, 0, measuredWidth, measuredHeight)
        if (mRotation == 1 || mRotation == 3) {
            measuredHeight = mContainer.measuredWidth
            measuredWidth = mContainer.measuredHeight
        }
        val r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888)
        r.eraseColor(Color.TRANSPARENT)
        val canvas = Canvas(r)
        if (mRotation != 0) {
            // do nothing
            when (mRotation) {
                1 -> {
                    canvas.translate(measuredWidth.toFloat(), 0f)
                    canvas.rotate(90f)
                }
                2 -> {
                    canvas.rotate(180f, (measuredWidth / 2).toFloat(), (measuredHeight / 2).toFloat())
                }
                else -> {
                    canvas.translate(0f, measuredHeight.toFloat())
                    canvas.rotate(270f)
                }
            }
        }
        mContainer.draw(canvas)
        return r
    }

    /**
     * Sets the child view for the icon.
     *
     *
     * If the view contains a [TextView] with the id "text", operations such as [ ][.setTextAppearance] and [.makeIcon] will operate upon that [TextView].
     */
    fun setContentView(contentView: View) {
        mRotationLayout.removeAllViews()
        mRotationLayout.addView(contentView)
        mContentView = contentView
        val view = mRotationLayout.getChildAt(0)
        mTextView = if (view is TextView) view else null
    }

    /**
     * Rotates the contents of the icon.
     *
     * @param degrees the amount the contents should be rotated, as a multiple of 90 degrees.
     */
    fun setContentRotation(degrees: Int) {
        mRotationLayout.setViewRotation(degrees)
    }

    /**
     * Rotates the icon.
     *
     * @param degrees the amount the icon should be rotated, as a multiple of 90 degrees.
     */
    fun setRotation(degrees: Int) {
        mRotation = (degrees + 360) % 360 / 90
    }

    /**
     * @return u coordinate of the anchor, with rotation applied.
     */
    val anchorU: Float
        get() = rotateAnchor(mAnchorU, mAnchorV)

    /**
     * @return v coordinate of the anchor, with rotation applied.
     */
    val anchorV: Float
        get() = rotateAnchor(mAnchorV, mAnchorU)

    /**
     * Rotates the anchor around (u, v) = (0, 0).
     */
    private fun rotateAnchor(u: Float, v: Float): Float {
        when (mRotation) {
            0 -> return u
            1 -> return 1 - v
            2 -> return 1 - u
            3 -> return v
            else -> {}
        }
        throw IllegalStateException()
    }

    /**
     * Sets the text color, size, style, hint color, and highlight color from the specified
     * `TextAppearance` resource.
     *
     * @param resId the identifier of the resource.
     */
    private fun setTextAppearance(context: Context?, resId: Int) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            mTextView?.setTextAppearance(resId)
        } else {
            mTextView?.setTextAppearance(context, resId)
        }
    }

    /**
     * Sets the text color, size, style, hint color, and highlight color from the specified
     * `TextAppearance` resource.
     *
     * @param resId the identifier of the resource.
     */
    fun setTextAppearance(resId: Int) {
        setTextAppearance(mContext, resId)
    }

    /**
     * Sets the style of the icon. The style consists of a background and text appearance.
     */
    fun setStyle(style: Int) {
        setTextAppearance(mContext, getTextStyle(style))
    }

    /**
     * Set the background to a given Drawable, or remove the background.
     *
     * @param background the Drawable to use as the background, or null to remove the background.
     */
    fun setBackground(background: Drawable?) {
        mContainer.background = background
        // Force setting of padding.
        // setBackgroundDrawable does not call setPadding if the background has 0 padding.
        if (background != null) {
            val rect = Rect()
            background.getPadding(rect)
            mContainer.setPadding(rect.left, rect.top, rect.right, rect.bottom)
        } else {
            mContainer.setPadding(0, 0, 0, 0)
        }
    }

    /**
     * Sets the padding of the content view. The default padding of the content view (i.e. text
     * view) is 5dp top/bottom and 10dp left/right.
     *
     * @param left   the left padding in pixels.
     * @param top    the top padding in pixels.
     * @param right  the right padding in pixels.
     * @param bottom the bottom padding in pixels.
     */
    fun setContentPadding(left: Int, top: Int, right: Int, bottom: Int) {
        mContentView.setPadding(left, top, right, bottom)
    }

    /**
     * Creates a new IconGenerator with the default style.
     */
    init {
        mContainer.orientation = LinearLayout.VERTICAL
        mContainer.layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        )
        mRotationLayout = RotationLayout(mContext)
        mRotationLayout.layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT
        )
        mTextView = TextView(mContext)
        //mTextView!!.includeFontPadding = false
        mRotationLayout.addView(
            mTextView,
            FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT
            )
        )
        val textViewLayoutParams = mTextView!!.layoutParams as FrameLayout.LayoutParams
        textViewLayoutParams.gravity = Gravity.CENTER_HORIZONTAL

        mContainer.addView(
            mRotationLayout,
            LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
            )
        )
        mContentView = mTextView!!
        setStyle(STYLE_DEFAULT)
    }


    companion object {
        const val STYLE_DEFAULT = 1
        const val STYLE_WHITE = 2
        const val STYLE_RED = 3
        const val STYLE_BLUE = 4
        const val STYLE_GREEN = 5
        const val STYLE_PURPLE = 6
        const val STYLE_ORANGE = 7
        private fun getStyleColor(style: Int): Int {
            return when (style) {
                STYLE_DEFAULT, STYLE_WHITE -> -0x1
                STYLE_RED -> -0x340000
                STYLE_BLUE -> -0xff6634
                STYLE_GREEN -> -0x996700
                STYLE_PURPLE -> -0x66cc34
                STYLE_ORANGE -> -0x7800
                else -> -0x1
            }
        }

        private fun getTextStyle(style: Int): Int {
            return when (style) {
                STYLE_DEFAULT, STYLE_WHITE -> R.style.BD_Bubble_TextAppearance_Dark
                STYLE_RED, STYLE_BLUE, STYLE_GREEN, STYLE_PURPLE, STYLE_ORANGE -> R.style.BD_Bubble_TextAppearance_Light
                else -> R.style.BD_Bubble_TextAppearance_Dark
            }
        }
    }
}